...loading
2024-11-29
기본적인 프로젝트 세팅을 해놓고 가장 먼저 손을 댄 부분은 헤더입니다. 심플하게 TATK-TOWN 이라는 로고를 좌측에 박아주고 우측에는 메뉴들을 나열했습니다. 헤더의 가장 우측에는 오늘 포스팅의 주제인 다크모드 버튼을 넣어줬습니다. 라이트모드의 색상은 역시나 블랙 앤 화이트가 깔끔하고 질리지 않을거 같아 큰 고민없이 적용해줬습니다.
저번 포스팅에서도 적은 것처럼 스타일은 module-css로 작성하고 있습니다. 위처럼 theme.css 파일을 만들어두고, 다크테마를 적용해주기 위해 data-theme을 사용하여 추가로 테마를 작성해줍니다.
//theme.css :root { --transition-duration: 0.1s; --transition-timing: ease-in-out; --primary-color: #ffffff; --secondary-color: #f7f7f5; --tertiary-color: #f0efea; --primary-color-r: #090a0d; --secondary-color-r: #1b1d24; --tertiary-color-r: #2d303b; --text-color: black; --text-color-2: #2d303b; /* 기타 기본 테마 */ } [data-theme="dark"] { --primary-color: #101720; --secondary-color: #15192d; --tertiary-color: #1a2140; --primary-color-r: #eeeeee; --secondary-color-r: #dddddd; --tertiary-color-r: #cccccc; --button-hover-bg: #f0efea; --button-hover-text: black; --text-color: #eeeeee; --text-color-2: #dddddd; }
당연한 이야기지만 좋은 UX를 위해 다크모드는 새로고침 시에도 사용자가 이전에 설정한 테마로 적용되어야 합니다. 따라서 웹스토리지에 최근에 사용된 테마를 저장해두는게 좋습니다. 로컬스토리지를 사용할 수 있지만 서버컴포넌트에서도 사용할 수 있는 쿠키를 사용하는게 더 좋은 방법이라 생각하여 쿠키를 사용했습니다. getThemeCookie, setThemeCookie라는 함수를 작성해 테마에 관련된 쿠키를 저장하고 업데이트할 수 있도록 분리해둡니다.
//theme-chage-button.tsx "use client"; import { useState, useEffect } from "react"; import { getThemeCookie, setThemeCookie } from "@/utils/cookies"; import DarkModeIcon from "./dark-mode-icon"; import LightModeIcon from "./light-mode-icon"; export type IConProps = { onClick: () => void; }; export default function ThemeChangeButton() { const [theme, setTheme] = useState(""); useEffect(() => { const savedTheme = getThemeCookie("theme"); document.documentElement.setAttribute("data-theme", savedTheme); setTheme(savedTheme); }, []); const handleTheme = () => { const nextTheme = theme === "dark" ? "light" : "dark"; document.documentElement.setAttribute("data-theme", nextTheme); setThemeCookie(nextTheme); setTheme(nextTheme); }; if (theme === "dark") { return <DarkModeIcon onClick={handleTheme} />; } else if (theme === "light") { return <LightModeIcon onClick={handleTheme} />; } }
위 코드는 다크모드 버튼 컴포넌트입니다. 리액트의 훅을 사용해야하기 때문에 클라이언트 컴포넌트로 선언해줍니다. 그리고 버튼의 onClick 속성에 handleTheme 함수를 적용하여 이제 버튼을 누를 때마다 css테마가 변경됩니다.
새로고침 시 기존의 테마를 유지하기 위해서 전역 layout.tsx 파일을 손봅니다. 앱이 처음으로 렌더링 될 때, 쿠키에 저장된 모드로 CSS를 렌더링하도록 합니다. 그리고 만약 저장된 쿠키가 없다면 light모드로 렌더링되도록 합니다. 아래가 해당 기능이 동작되도록 만든 코드입니다.
//layout.tsx import Header from "@/components/header/header"; import { cookies } from "next/headers"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; const checkTheme = () => { const nextCookies = cookies(); const themeCookie = nextCookies.get("theme")?.value || ""; return themeCookie; }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { const theme = checkTheme(); return ( <html lang="en" className="background" data-theme={theme}> <body className={inter.className}> <Header /> {children} </body> </html> ); }
처음 구현하는 다크모드였지만, 무난하게 구현할 수 있었습니다. 나중에 보완해야할 부분이 있다 새로고침 시, 모드의 아이콘이 깜빡이는 현상이 있습니다. 테마 버튼 컴포넌트에서 상태값에 따라 다른 아이콘을 렌더링하도록 만들었는데, 초기 상태값을 null로 설정했기 때문에 새로고침 시 잠깐 깜빡이는 현상이 발생합니다. 초기 상태값으로 cookie값을 불러오려 했는데 이게 잘 안되더라구요?.. 다른 기능을 개발해야하기에 일단은 아이콘에 애니메이션을 적용해 자연스럽게 만들어 뒀습니다만,, 추후에는 이를 정면으로 부딪혀 보완해보려 합니다.
Comments